package com.cn.lp.export.client.entity;

import com.cn.lp.export.client.ExportClient;
import com.cn.lp.export.client.base.annotation.ExportField;
import com.cn.lp.export.client.base.context.AttrKey;
import com.cn.lp.export.client.base.context.Attributes;
import com.cn.lp.export.client.base.utils.BeanUtils;
import com.cn.lp.export.client.export.CreateExportTaskResponse;
import com.cn.lp.export.client.export.Export2EntityMapping;
import com.cn.lp.export.client.export.ExportData;
import com.cn.lp.export.client.utils.JSONAide;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * Description: 请描述你的文件
 *
 * @author wuqirong
 * @date 2020-10-21
 * <p>
 * All rights Reserved, Designed www.xiao100.com
 */
@Slf4j
public abstract class ListExportEventHandler<C, V, E> implements ExportEventHandler {

    public static AttrKey<BaseExportInfoParam.ExportEventVo> EVENT_SOURCE = Attributes.createKey("EVENT_SOURCE");

    /**
     * 事件名
     */
    private String name;

    private Class<E> sheetClass;

    protected ListExportEventHandler(String name, Class<E> sheetClass) {
        this.sheetClass = sheetClass;
        this.name = name;
    }

    /**
     * 获取数组总数
     *
     * @param condition 查询条件
     * @param context   上下文
     * @return
     */
    public abstract Long getTotalCount(C condition, Attributes context);

    /**
     * 获取数组列表
     *
     * @param condition 查询条件
     * @param pageNo    页码
     * @param pageSize  每页大小
     * @param context   上下文
     * @return
     */
    public abstract List<V> getVoList(C condition, Integer pageNo, Integer pageSize, Attributes context);

    /**
     * 构建导出Excel对象
     *
     * @param vo
     * @param context
     * @return
     */
    public abstract List<E> format2Sheet(V vo, Attributes context);

    /**
     * 初始化上下文
     *
     * @param context
     * @param source
     * @param condition
     */
    public void initContext(Attributes context, BaseExportInfoParam.ExportEventVo source, C condition) {
        context.setAttributeIfNoKey(EVENT_SOURCE, source);
    }

    /**
     * 格式化数据前，迭代数据前
     *
     * @param voList
     * @param context
     */
    public void preFormat(List<V> voList, Attributes context) {

    }

    /**
     * 查询前，迭代查询前
     *
     * @param context
     * @param pageSize
     * @param maxPageNo
     */
    public void preSearch(Attributes context, Integer pageSize, Long maxPageNo) {

    }

    /**
     * 获取每页大小
     *
     * @param totalCount
     * @param context
     * @return
     */
    public Integer getPageSize(Long totalCount, Attributes context) {
        return totalCount <= 6000 ? totalCount.intValue() : 5000;
    }

    protected List<Export2EntityMapping> getExportFieldMap(Class<E> sheetClass, C condition, Attributes context) {
        List<String> ignoreHeadList = getIgnoreHead(sheetClass, condition, context);
        List<Export2EntityMapping> exportFieLidList = new ArrayList<>();
        List<Field> fields = new ArrayList<>();
        ReflectionUtils.doWithFields(sheetClass, fields::add,
            field -> field.getAnnotation(ExportField.class) != null
        );
        Collections.sort(fields, Comparator.comparingInt(o -> o.getAnnotation(ExportField.class).index()));
        for (Field field : fields) {
            if (!ignoreHeadList.contains(field.getName())) {
                exportFieLidList.add(Export2EntityMapping.entityMapping(field.getAnnotation(ExportField.class).value(), field.getName()));
            }
        }
        return exportFieLidList;
    }

    protected List<String> getIgnoreHead(Class<E> sheetClass, C condition, Attributes context) {
        return Collections.emptyList();
    }

    protected long getUploadTotalNum(long totalDataNum, Attributes context) {
        return totalDataNum;
    }

    protected long getUploadDataNum(long dataNum, Attributes context) {
        return dataNum;
    }

    protected abstract ExportClient getExportClient();

    @Override
    public BaseExportInfoParam.ExportEventResultVo deal(BaseExportInfoParam.ExportEventVo source) {
        BaseExportInfoParam.ExportEventResultVo exportEventResultVo = new BaseExportInfoParam.ExportEventResultVo();
        C condition = (C) source.getCondition();
        // 获取导出条件
        log.info("导出{} ConditionVo={}", name, JSONAide.toJson(condition));
        Attributes context = new Attributes();
        this.initContext(context, source, condition);
        // 查询总数
        Long totalCount = this.getTotalCount(condition, context);
        CreateExportTaskResponse response = getExportClient().createExportTask(
            source.getBusinessType().getFileName(),
            getExportFieldMap(this.sheetClass, condition, context),
            getUploadTotalNum(totalCount, context)
        );
        //异步提交导出任务
        CompletableFuture.runAsync(() -> {
            try {
                if (totalCount > 0) {
                    // 分页次数
                    Integer pageSize = this.getPageSize(totalCount, context);
                    Long maxPageNo = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
                    maxPageNo = maxPageNo <= 0 ? 1 : maxPageNo;
                    preSearch(context, pageSize, maxPageNo);
                    // 异步执行
                    for (int i = 0; i < maxPageNo; i++) {
                        List<V> voList = this.getVoList(condition, i, pageSize, context);
                        preFormat(voList, context);
                        List<Map<String, Object>> dataList = new ArrayList<>();
                        voList.forEach(vo ->
                            dataList.addAll(this.format2Sheet(vo, context)
                                .stream()
                                .map(BeanUtils::beanToMap)
                                .collect(Collectors.toList())));
                        getExportClient().uploadExportData(
                            Long.parseLong(response.getTaskId()),
                            ExportData.of(i, dataList),
                            getUploadDataNum(voList.size(), context)
                        );
                    }
                } else {
                    getExportClient().uploadExportData(
                        Long.parseLong(response.getTaskId()),
                        ExportData.of(0, Collections.emptyList()),
                        getUploadDataNum(0, context)
                    );
                }
            } catch (Exception e) {
                log.error("导出{}异常", this.name, e);
                getExportClient().cancelTask(Long.parseLong(response.getTaskId()));
            }
        });
        exportEventResultVo.setResultUrl(response.getResultUrl());
        return exportEventResultVo;
    }

}
